#ifndef JOB_HEADER_FLAG
#define JOB_HEADER_FLAG
// The included directories
#include "option.h"
//#include "getvoltages.h"
#include <stdio.h> 		// Needed before NIDAQmx.h with MinGW
#include "NIDAQmx.h"
#include "mathlink.h"
#include <math.h>
#include <string>
#include <algorithm>
#include <sstream>
#include <map>
#include <vector>
#include<string>
using namespace std;			// bad idea! put in both cpp files? use using declaration in cpp files

class persistJobHolder;			// this declaration needed for Job Class declaration

class Job
{
public:
					Job(string taskname = "Default Job");		//old
					Job(string taskname, persistJobHolder&);
					Job(string taskname, persistJobHolder&, option&);
					Job(Job&);
	virtual			~Job();
	virtual Job*	clone()=0;				// returns pointer like new does

	option&			getOptions(){return opt;}
	long			getoptL(string name) {return opt.getL(name);}
	double			getoptD(string name) {return opt.getD(name);}
	string			getoptS(string name) {return opt.getS(name);}
	double*			getoptDArray(string name) {return opt.getDArray(name);}

	TaskHandle		getTaskHandle() {return taskHandle;} // constant reference?
	void			clearTaskHandle(){taskHandle = 0;}		// NOT clearTask
	int				startTask(TaskHandle taskHandle);
	void virtual	continueTask(double arg1 = 0.0, double arg2 = 0.0);
	void virtual	configureClock(double numPeriods = 1.0);
	bool virtual	sendToMathematica()=0;
	int32 iErr;			// derived class must be able to change
private:
	TaskHandle			taskHandle;
	option				opt;
};
// defined in file scope in job.cpp
// not in header? int Job::numTasks =0;

class InputJob : public Job
{
	// at least for now lets use inputjob for single input
public:
						InputJob(string taskname = "Default Job");
						InputJob(string taskname,persistJobHolder&);
						InputJob(string taskname, persistJobHolder&, option&);
						InputJob(InputJob&);
	virtual				~InputJob();
	virtual	InputJob*	clone() {return new InputJob(*this);}
	void virtual		continueTask(double arg1 = 0.0, double arg2 = 0.0);
	bool virtual		isDoneNoError(){return true;}
	int	virtual			readData();
	bool virtual		sendToMathematica();
	double*				getSampleArray(){return sampleArray;}
	long				getSampleArraySize(){return sampleArraySize;}

private:
	void		createAIChan();
	double		*sampleArray;
	long		sampleArraySize;
};

class BufferedInputJob : public InputJob
{
public:
						BufferedInputJob(string taskname, persistJobHolder&);
						BufferedInputJob(string taskname, persistJobHolder&, option&);
						BufferedInputJob(BufferedInputJob& old);
	virtual				~BufferedInputJob();
	void virtual		continueTask(double arg1 = 0.0, double arg2 = 0.0);
	bool virtual		isDoneNoError();
	void				setTransferMech();
private:
	;	// no new data members
};

class SoftTrigJob : public BufferedInputJob
{
public:
						SoftTrigJob(string taskname, persistJobHolder&);
						SoftTrigJob(string taskname, persistJobHolder&, option&);
						SoftTrigJob(SoftTrigJob& old);
	virtual				~SoftTrigJob();
	bool virtual		sendToMathematica();
	long				waitForTrigger();
	void				readPreTrigger();
	void				readPostTrigger();

//	bool virtual		isDoneNoError();
private:
	double*				pTestingArray;		// testing and posttrigger share memory
	long				testingArraySize;
	double*				pPreTrigArray;		//use sample array is pretriggersamples
	long				preTrigArraySize;					
	void				doConstruction();
	long				readPastTrig;
};

class DigitalTrigJob : public BufferedInputJob
{
public:
						DigitalTrigJob(string taskname, persistJobHolder&);
						DigitalTrigJob(string taskname, persistJobHolder&, option&);
						DigitalTrigJob(DigitalTrigJob& old);
	virtual				~DigitalTrigJob();
//	bool virtual		sendToMathematica();
//	bool virtual		isDoneNoError();
private:
		void				doConstruction();
};

class OutputJob : public Job
{
public:
						OutputJob(string taskname, persistJobHolder&);
						OutputJob(string taskname, persistJobHolder&, option&);
						OutputJob(OutputJob&);
	virtual			~OutputJob();
	virtual			OutputJob*	clone(){return new OutputJob(*this);}
	bool virtual	sendToMathematica();
	void virtual	continueTask(double ch0Volt = 0.0, double ch1Volt = 0.0);
	void virtual	createAOChan();
	void				writeData();
	float64			dataStorage[2];
private:
	void				doConstruction();

};
class BufferedOutputJob : public OutputJob
{
public:
						BufferedOutputJob(string taskname, persistJobHolder&);
						BufferedOutputJob(string taskname, persistJobHolder&, option&);
						BufferedOutputJob(BufferedOutputJob&);
	virtual				~BufferedOutputJob();
						virtual BufferedOutputJob*	clone(){return new BufferedOutputJob(*this);}
	void virtual		setTransferMech();
	void virtual		waveformToBuffer();
	void virtual		continueTask(double ch0Volt = 0.0, double ch1Volt = 0.0);
private:
	void				doConstruction();
};

struct JobState
	// If JobState is zero then when task is done it is cleared
	// if not zero task persist to be run again;
{
	Job*	pJob;		
	long	runAgainCount;				// 0 = runOnce, 1 = runContinuous
};										// runRepeated =0 --> finite task, 
										// quit if task is done
class persistJobHolder
/*	Inteded to be used as a global class
	When changing a job to a persistant job set taskHandle to 0 on old job
	 so that destructor can be called on old job without clearing the task
	 need copy constructor for each class since we won't in general now the details
	 all persistant jobs created with new, so ok to call delete
	 copy constructor including call to base class copy constructor
	 persistJobHolder needs to know message level
*/
{
public:
	~persistJobHolder();
	Job&		changeToPersist(Job &j, long runAgainCount);		// wiil know own TaskHandle
	Job&		getJob(TaskHandle task){return *(jobList[task].pJob);}  // lets try retruning reference for ease of use?
	void		runJob(TaskHandle task);
	void		clearFinishedJobs();
	void		clearJob(TaskHandle task);
	map<TaskHandle,JobState>		getJobList() { return jobList;}
private:
	map<TaskHandle, JobState> jobList;
};

#endif


